﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using XmlSockets;
using NUnit.Framework;
using XmlSockets.Internal;
using System.Xml.Linq;
using System.Xml;
using System.IO;

namespace LibXmlSocket_UnitTest
{
    [TestFixture]
    public class XmlSocketUnitTest
    {
        #region コンストラクタ
        [Test]
        public void Constructor()
        {
            var socket = new XmlSocket(new DummyInternalSocket(), new XmlSocketCreateInfo());
            
        }
        #endregion

        #region 送信



        internal  void  SendCheck(XmlSocketSendHandler handler,CreateXmlSocketSendContentHandler createHandler)
        {
            var socket = CreateXmlSocket();
            socket.ConnectAsync("dummydomain", 445).GetAwaiter().GetResult();
            socket.SendAsync(handler).GetAwaiter().GetResult();
            
        }

        [TestCase("test")]
        public void SendTest(string value)
        {
            SendCheck((v) => { v.WriteName(value); }, (v, l) => { return new TestSendContent(v, l); });
        }

        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void SendNotWriteTest()
        {
            SendCheck((v) => { }, (v, l) => 
            { 
                return new TestSendContent(v, l); 
            });
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void SendNullCheck()
        {
            SendCheck(null, null);

        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void SendEventNullCheck()
        {
            SendCheck(null, (v, l) => { return new TestSendContent(v, l); });
        }
        #endregion

        #region 受信

        public void ReceiveTest(XmlSocketReceiveHandler receiveHandler)
        {
            var socket = CreateXmlSocket();
            socket.ConnectAsync("dummydomain", 445).GetAwaiter().GetResult();
            socket.ReceiveAsync(receiveHandler).GetAwaiter().GetResult();

        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void ReceiveNullCheck()
        {
            ReceiveTest(null);
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void ReceiveHandlerNullCheck()
        {
            ReceiveTest(null);
        }



        [Test]
        public void ReceiveOnParseExceptionNullAbleTest()
        {
            ReceiveTest((r) => { });
        }

        [Test]
        public void BadEnumeLoopTest()
        {

            var testMessageUnion = new List<string>[2];
            var testMessage1 = new List<string>();
            var testMessage2 = new List<string>();

            testMessageUnion[0] = testMessage1;
            testMessageUnion[1] = testMessage2;


            testMessage1.Add("<testMessage1 param1=\"value\" />\0");
            testMessage1.Add("<testMe");

            testMessage2.Add("ssage2 param2=\"value2\">body</testMessage2>\0");
            testMessage2.Add("<testmessage3 param3=\"value\" />\0");
            var dummyInternalSocket = new DummyInternalSocket();
            var xmlSocket = CreateXmlSocket(dummyInternalSocket);
            xmlSocket.ConnectAsync("dummydomain", 445).GetAwaiter().GetResult();


            dummyInternalSocket.AddReceiveMessages(testMessage1.ToArray());
            xmlSocket.ReceiveAsync((messageEnumerator) =>
            {
                var enumrator = messageEnumerator.GetEnumerator();
                enumrator.MoveNext();
                enumrator.MoveNext();
                enumrator.MoveNext();
    

            }).GetAwaiter().GetResult();


            dummyInternalSocket.AddReceiveMessages(testMessage2.ToArray());
            xmlSocket.ReceiveAsync((messageEnumeable) =>
            {
                var enumrator = messageEnumeable.GetEnumerator();
                enumrator.MoveNext();
                enumrator.MoveNext();
                Assert.AreEqual(XElement.Parse("<testmessage3 param3=\"value\" />").ToString(), XElement.Load(enumrator.Current.Reader).ToString());
            }).GetAwaiter().GetResult();
            
        }
        

        public void ReceiveLoopTest(List<string>[] testMessagesUnion,Action<int,int,XmlMessage> onCheck)
        {
            var dummyInternalSocket = new DummyInternalSocket();
            var xmlSocket = CreateXmlSocket(dummyInternalSocket);
            xmlSocket.ConnectAsync("dummydomain", 445).GetAwaiter().GetResult();

            for (int unionIndex = 0; unionIndex < testMessagesUnion.Length;unionIndex++ )
            {
                var messages = testMessagesUnion[unionIndex];
                dummyInternalSocket.AddReceiveMessages(messages.ToArray());
                List<XElement> receiveMessageList = new List<XElement>();
                int index = 0;
                xmlSocket.ReceiveAsync((messageEnumerator) =>
                {
                    foreach (var r in messageEnumerator)
                    {
                        
                        onCheck(unionIndex,index,r);
                        index++;
                    }
                    
                }).GetAwaiter().GetResult();

 


            }
        }


        [Test]
        public void ReceiveLoopBadTest()
        {
            var testMessageUnion = new List<string>[2];
            var testMessage1 = new List<string>();
            var testMessage2 = new List<string>();
            testMessageUnion[0] = testMessage1;
            testMessageUnion[1] = testMessage2;

            testMessage1.Add("badMessageaaaa\0");
            testMessage2.Add("badmessage2\0");

            ReceiveLoopTest(testMessageUnion, (u,i,r) =>
            {
                Assert.AreNotEqual(r.Reader, null);
            });
        }

        [Test]
        public void ReceiveLoopSucceedTest()
        {

            var testMessageUnion = new List<string>[2];
            var testMessage1 = new List<string>();
            var testMessage2 = new List<string>();

            testMessageUnion[0] = testMessage1;
            testMessageUnion[1] = testMessage2;


            testMessage1.Add("<testMessage1 param1=\"value\" />\0");
            testMessage1.Add("<testMe");

            testMessage2.Add("ssage2 param2=\"value2\">body</testMessage2>\0");
            testMessage2.Add("<testmessage3 param3=\"value\" />\0");
            bool testflag1 = false;
            bool testflag2 = false;
            bool testflag3 = false;
            ReceiveLoopTest(testMessageUnion, (unionIndex,index, message) =>
            {

                var xmlSocketStyleMessage = XElement.Load(message.Reader).ToString() + "\0";
                switch (unionIndex)
                {
                    case 0:

                        switch (index)
                        {
                            case 0:
                                Assert.AreEqual(testMessage1[0], xmlSocketStyleMessage);
                                testflag1 = true;
                                break;

                        }

                        

                        break;


                    case 1:

                        switch (index)
                        {
                            case 0:
                                Assert.AreEqual(testMessage1[1] + testMessage2[0], xmlSocketStyleMessage);
                                testflag2 = true;
                                break;


                            case 1:
                                Assert.AreEqual(testMessage2[1], xmlSocketStyleMessage);
                                testflag3 = true;
                                break;
                        }

                        break;

                }

                

            });
            Assert.IsTrue(testflag1);
            Assert.IsTrue(testflag2);
            Assert.IsTrue(testflag3);
        }


        [TestCase(65535)]
        [TestCase(-1)]
        public void MaxReceiveSizeTest(int size)
        {
            XmlSocketCreateInfo createInfo = new XmlSocketCreateInfo();
            createInfo.MaxMessageSize = size;
            var xmlSocket = new XmlSocket(TestCreateInternalSocket(), createInfo);
        }

        [TestCase(100)]
        [ExpectedException(typeof(InvalidOperationException))]
        public void MaxReceiveOutSizeTest(int size)
        {
            
            DummyInternalSocket dummySocket = new DummyInternalSocket();
            dummySocket.AddReceiveMessage("<testmessage test=\"vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\"/>");
            XmlSocketCreateInfo createInfo = new XmlSocketCreateInfo();
            createInfo.MaxMessageSize = size;
            var xmlSocket = new XmlSocket(dummySocket, createInfo);
            xmlSocket.ConnectAsync("dummydomein", 445).GetAwaiter().GetResult();
            xmlSocket.ReceiveAsync((messages) => { messages.GetEnumerator().MoveNext(); }).GetAwaiter().GetResult();
        }

        [TestCase(-1)]
        [TestCase(65535)]
        public void MaxReceiveSucceedTest(int size)
        {
            MaxReceiveOutSizeTest(size);
        }

        #endregion

        #region 接続
        [TestCase(null)]
        [ExpectedException(typeof(ArgumentNullException))]
        public void ConnectNullCheck(string hostName)
        {
            ConnectTest(hostName, 1025);
        }

        [TestCase(-1)]
        [TestCase(65536)]
        [ExpectedException(typeof(ArgumentException))]
        public void ConnectPortNoCheck(int port)
        {
            ConnectTest("testHostName",port);
        }

        [TestCase("testhost.jp",445)]
        public void ConnectTest(string hostName, int port)
        {
            var socket = CreateXmlSocket();
            socket.ConnectAsync(hostName, port).GetAwaiter().GetResult();
        }

        #endregion

        private DummyInternalSocket TestCreateInternalSocket()
        {
            return new DummyInternalSocket();
        }

        private XmlSocket CreateXmlSocket()
        {
            return new XmlSocket(TestCreateInternalSocket(),new XmlSocketCreateInfo());
        }

        private XmlSocket CreateXmlSocket(DummyInternalSocket socket)
        {
            return new XmlSocket(socket,new XmlSocketCreateInfo());
        }
    }
}
